#include "nasm/visitor/instGenerator.h"
#include "nasm/report/report.h"
#include "nasm/elf/elf.h"
#include "nasm/bitTools/bitTools.h"

namespace dspnasm {

    InstGenerator::InstGenerator(std::shared_ptr<SymbolTable> symbolTable, std::shared_ptr<ElfFile> elfFile) {
        m_symbolTable = symbolTable;
        m_elfFile = elfFile;
        m_generator = std::make_shared<Generator>();
        m_segmentName = "";
        m_assemblyAddress = m_offset = 0;
        m_curSize = 32;
    }

    antlrcpp::Any InstGenerator::visitType(NasmParser::TypeContext *ctx) {
        std::cout << "type" << std::endl;
        if (ctx->Id()) {
            auto symbol = m_symbolTable->getSymbol(ctx->Id()->getText());
            if (symbol == nullptr) {
                fatalMacro(("未定义的符号 : " + ctx->Id()->getText()).c_str());
            }
            if (!symbol->m_isEqu) {
                m_elfFile->addRel(m_segmentName, m_offset, ctx->Id()->getText(), R_386_32);
            }
        }
        return 0;
    }

    antlrcpp::Any InstGenerator::visitOperand(NasmParser::OperandContext *ctx) {
        std::cout << "operand" << std::endl;
        std::shared_ptr<Operand> operand;
        // operand  : type
        if (ctx->type()) {
            auto num = ctx->type()->num();
            if (num) {
                u32 val = visit(num).as<u32>();
                if (ctx->type()->sign()->Sub()) {
                    val = 0 - val;
                }
                operand = std::make_shared<ImmediateOp>(val);
            }
            auto id = ctx->type()->Id();
            if (id) {
                auto symbol = m_symbolTable->getSymbol(id->getText());
                if (symbol->m_isEqu) {
                    operand = std::make_shared<ImmediateOp>(vectorToU32(symbol->m_data));
                } else {
                    operand = std::make_shared<ImmediateOp>(symbol->m_offset);
                    m_elfFile->addRel(m_segmentName, m_offset, id->getText(), R_386_32);
                }
            }
            auto Char = ctx->type()->Char();
            if (Char) {
                char ch = Char->getText()[1];
                operand = std::make_shared<ImmediateOp>(static_cast<u32>(ch));
            }
        } else if (ctx->regs()) {
            auto regs = ctx->regs()->reg();
            if (regs.size() == 1) {
                operand = std::make_shared<SingleRegOp>(regs[0]->getText());
            } else if (regs.size() == 2) {
                operand = std::make_shared<DoubleRegOp>(regs[1]->getText(), regs[0]->getText());
            }
        }
        return operand;
    }

    antlrcpp::Any InstGenerator::visitDeclare(NasmParser::DeclareContext *ctx) {
        std::cout << "declare" << std::endl;
        if (ctx->Section()) {
            // Section Id
            m_segmentName = ctx->Id()->getText();
            m_assemblyAddress += (m_offset + (4 - m_offset % 4) % 4);
            m_offset = 0;
        } else if (ctx->Global() || ctx->Externed() || ctx->Id()) {
        } else if (ctx->Lbrace()) {
            u32 num = visit(ctx->num()).as<u32>();
            if (num == 16 || num == 32) {
                m_curSize = num;
            } else {
                fatalMacro("指令尺寸只能为16或32位");
            }
        } else if (ctx->Times()) {
            // Times num size type
            u32 num = visit(ctx->num()).as<u32>();
            u32 size = 0;
            if (ctx->size()->Db()) {
                size = 1;
            } else if (ctx->size()->Dw()) {
                size = 2;
            } else if (ctx->size()->Dd()) {
                size = 4;
            } else if (ctx->size()->Dq()) {
                size = 8;
            }

            // sign ? num
            auto type = ctx->type();
            if (type->num()) {
                u32 data = visit(type->num()).as<u32>();
                if (type->sign()->Sub()) {
                    data = 0 - data;
                }
                auto bytes = splitI32IntoByte(data, size);

                for (u32 i = 0; i < num; ++i) {
                    m_elfFile->writeBytes(m_segmentName, bytes);
                    m_offset += size;
                }
            } else if(type->Char()){
                u32 data = static_cast<u32>(type->Char()->getText()[1]);
                auto bytes = splitI32IntoByte(data, size);
                for (u32 i = 0; i < num; ++i) {
                    m_elfFile->writeBytes(m_segmentName, bytes);
                    m_offset += size;
                }
            }
        } else {
            // size data
            u32 size = 0;
            if (ctx->size()->Db()) {
                size = 1;
            } else if (ctx->size()->Dw()) {
                size = 2;
            } else if (ctx->size()->Dd()) {
                size = 4;
            } else if (ctx->size()->Dq()) {
                size = 8;
            }

            m_offset += size;
        }
        return 0;
    }

    antlrcpp::Any InstGenerator::visitInstructionPacket(NasmParser::InstructionPacketContext *ctx) {
        std::cout << "instructionPacket" << std::endl;
        auto instructionPacket = ctx->instruction();
        auto size = instructionPacket.size();
        bool p = true;

        for (u32 i = 0; i < size; ++i) {
            if (i == size - 1) {
                p = false;
            }

            if (instructionPacket[i]->threeOp()) {
                std::string opName = instructionPacket[i]->threeOp()->getText();
                std::string funcUnit = instructionPacket[i]->FuncUnit()->getText();
                auto op0 = visit(instructionPacket[i]->operand(0)).as<std::shared_ptr<Operand>>();
                auto op1 = visit(instructionPacket[i]->operand(1)).as<std::shared_ptr<Operand>>();
                auto op2 = visit(instructionPacket[i]->operand(2)).as<std::shared_ptr<Operand>>();
                auto instruction = m_generator->gen3Op(opName, funcUnit, {op0, op1, op2}, p);
                m_elfFile->writeBytes(m_segmentName, instruction->transformIntoByte32());
            }
        }
        return 0;
    }


    antlrcpp::Any InstGenerator::visitNum(dspnasm::NasmParser::NumContext *ctx) {
        std::cout << "num" << std::endl;
        // num : Decimal
        //     | Octal
        //     | Hex
        //     | Binary
        //     ;
        u32 data = 0;
        if (ctx->Binary()) {
            data = std::stoi(ctx->Binary()->getText().substr(2), nullptr, 2);
        } else if (ctx->Octal()) {
            data = std::stoi(ctx->Octal()->getText(), nullptr, 8);
        } else if (ctx->Decimal()) {
            data = std::stoi(ctx->Decimal()->getText(), nullptr, 10);
        } else if (ctx->Hex()) {
            data = std::stoi(ctx->Hex()->getText(), nullptr, 16);
        }
        return data;
    }
}